package cmd

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"os"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/backoff"

	pb "golang.aliyun-inc.com/agit/satellite-proto/go"
)

type SpaceXParams struct {
	Enabled          bool      `json:"enabled"`
	Operation        string    `json:"operation"`
	ConnType         string    `json:"conn_type"`
	Time             time.Time `json:"-"`
	SpaceXHost       string    `json:"spacex_host"`
	SpaceXPort       int64     `json:"spacex_port"`
	TransactionID    uint64    `json:"transaction_id"`
	TransactionVoter string    `json:"transaction_voter"`
	MainHooker       bool      `json:"main_hooker"`
}

const spacexEnv = "SPACEX_PAYLOAD"

func voteCommands(ctx context.Context, params *SpaceXParams, commands []*pb.Command) error {
	if len(commands) == 0 {
		// skip voting if no ref updates
		return fmt.Errorf("no ref updates collected")
	}

	conn, err := grpc.Dial(fmt.Sprintf("%s:%d", params.SpaceXHost, params.SpaceXPort),
		grpc.WithInsecure(),
		grpc.WithConnectParams(grpc.ConnectParams{
			Backoff: backoff.Config{
				BaseDelay:  time.Microsecond * 100,
				Multiplier: 1.6,
				Jitter:     0.2,
				MaxDelay:   time.Second * 3,
			},
			MinConnectTimeout: time.Second * 5,
		}),
	)
	if err != nil {
		return err
	}
	defer conn.Close()

	cli := pb.NewTransactionClient(conn)
	res, err := cli.VoteTransaction(ctx, &pb.VoteTransactionRequest{
		Name:          params.TransactionVoter,
		TransactionId: params.TransactionID,
		Commands:      commands,
	})
	if err != nil {
		return err
	}

	if res.State != pb.VoteTransactionResponse_COMMIT {
		return fmt.Errorf("transaction not committed(%s)", res.State.String())
	}

	return nil
}

func getSpaceXParamsFromEnv() (*SpaceXParams, error) {
	res := &SpaceXParams{}
	payload := os.Getenv(spacexEnv)
	if payload == "" {
		return res, nil
	}

	raw, err := base64.StdEncoding.DecodeString(payload)
	if err != nil {
		return nil, err
	}

	if err := json.Unmarshal(raw, res); err != nil {
		return nil, err
	}

	return res, nil
}
